#include "sh1106.h"
#include <string.h>

#define BUFFER_SIZE (OLED_SIZE_X * OLED_SIZE_Y / 8)

#define OLED_DC_0()     HAL_GPIO_WritePin(GPIOA, GPIO_PIN_2, GPIO_PIN_RESET)
#define OLED_DC_1()     HAL_GPIO_WritePin(GPIOA, GPIO_PIN_2, GPIO_PIN_SET)
#define OLED_CS_0()     HAL_GPIO_WritePin(GPIOA, GPIO_PIN_0, GPIO_PIN_RESET)
#define OLED_CS_1()     HAL_GPIO_WritePin(GPIOA, GPIO_PIN_0, GPIO_PIN_SET)
#define OLED_RES_0()    HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, GPIO_PIN_RESET)
#define OLED_RES_1()    HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, GPIO_PIN_SET)
#define OLED_CLK_0()    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, GPIO_PIN_RESET)
#define OLED_CLK_1()    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, GPIO_PIN_SET)
#define OLED_DAT_0()    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_9, GPIO_PIN_RESET)
#define OLED_DAT_1()    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_9, GPIO_PIN_SET)

uint8_t gOledBuf[BUFFER_SIZE];

static void OLED_Reset(void)
{
    OLED_RES_0();
    HAL_Delay(100);
    OLED_RES_1();
}

void WriteCmd(uint8_t cmd)
{
    OLED_CS_0();
    OLED_DC_0();

    for(uint8_t i = 0; i < 8; i++)
    {
        OLED_CLK_0();

        if(cmd & 0x80)
        {
            OLED_DAT_1();
        }
        else
        {
            OLED_DAT_0();
        }

        OLED_CLK_1();
        cmd <<= 1;
    }

    OLED_CS_1();
}

void WriteDat(uint8_t dat)
{
    OLED_CS_0();
    OLED_DC_1();

    for(uint8_t i = 0; i < 8; i++)
    {
        OLED_CLK_0();

        if(dat & 0x80)
        {
            OLED_DAT_1();
        }
        else
        {
            OLED_DAT_0();
        }

        OLED_CLK_1();
        dat <<= 1;
    }

    OLED_CS_1();
}

void SH1106_Init(void)
{
    // GPIO config

    // Reset OLED
    OLED_Reset();

    // Config OLED
    WriteCmd(0xAE);//--turn off oled panel
    WriteCmd(0x00);//---set low column address
    WriteCmd(0x10);//---set high column address
    WriteCmd(0x40);//--set start line address  Set Mapping RAM Display Start Line (0x00~0x3F)
    WriteCmd(0x81);//--set contrast control register
    WriteCmd(0xCF); // Set SEG Output Current Brightness
    WriteCmd(0xA1);//--Set SEG/Column Mapping     0xa0???? 0xa1??
    WriteCmd(0xC8);//Set COM/Row Scan Direction   0xc0???? 0xc8??
    WriteCmd(0xA6);//--set normal display
    WriteCmd(0xA8);//--set multiplex ratio(1 to 64)
    WriteCmd(0x3f);//--1/64 duty
    WriteCmd(0xD3);//-set display offset	Shift Mapping RAM Counter (0x00~0x3F)
    WriteCmd(0x00);//-not offset
    WriteCmd(0xd5);//--set display clock divide ratio/oscillator frequency
    WriteCmd(0x80);//--set divide ratio, Set Clock as 100 Frames/Sec
    WriteCmd(0xD9);//--set pre-charge period
    WriteCmd(0xF1);//Set Pre-Charge as 15 Clocks & Discharge as 1 Clock
    WriteCmd(0xDA);//--set com pins hardware configuration
    WriteCmd(0x12);
    WriteCmd(0xDB);//--set vcomh
    WriteCmd(0x40);//Set VCOM Deselect Level
    WriteCmd(0x20);//-Set Page Addressing Mode (0x00/0x01/0x02)
    WriteCmd(0x02);//
    WriteCmd(0x8D);//--set Charge Pump enable/disable
    WriteCmd(0x14);//--set(0x10) disable
    WriteCmd(0xA4);// Disable Entire Display On (0xa4/0xa5)
    WriteCmd(0xA6);// Disable Inverse Display On (0xa6/a7)
    WriteCmd(0xAF);//--turn on oled panel

    WriteCmd(0xAF); /*display ON*/
}

void SH1106_DisplayOn(void)
{
    WriteCmd(0X8D);  //SET DCDC
    WriteCmd(0X14);  //DCDC ON
    WriteCmd(0XAF);  //DISPLAY ON
}

void SH1106_DisplayOff(void)
{
    WriteCmd(0X8D);  //SET DCDC
    WriteCmd(0X10);  //DCDC OFF
    WriteCmd(0XAE);  //DISPLAY OFF
}

void SH1106_BufferFlush(void)
{
    for(uint8_t i = 0; i < 8; i++)
    {
        WriteCmd (0xb0+i);
        WriteCmd (0x00); 
        WriteCmd (0x10);
        for(uint16_t j = 0; j < 128; j++)
        {
            WriteDat(gOledBuf[i*128 + j]);
        }
    }
}

void SH1106_Clear(void)
{
    memset(gOledBuf, 0xff, sizeof(gOledBuf));
    SH1106_BufferFlush();
}



void SH1106_WriteM8_A1(uint8_t *pDat, int len)
{
    for(int j = 0; j < len; j++)
    {
        WriteDat(pDat[j]);
    }
}

void SH1106_Write8_A1(uint8_t dat)
{
    OLED_CS_0();
    OLED_DC_1();

    for(uint8_t i = 0; i < 8; i++)
    {
        OLED_CLK_0();

        if(dat & 0x80)
        {
            OLED_DAT_1();
        }
        else
        {
            OLED_DAT_0();
        }

        OLED_CLK_1();
        dat <<= 1;
    }

    OLED_CS_1();
}

void SH1106_Write8_A0(uint8_t cmd)
{
    OLED_CS_0();
    OLED_DC_0();

    for(uint8_t i = 0; i < 8; i++)
    {
        OLED_CLK_0();

        if(cmd & 0x80)
        {
            OLED_DAT_1();
        }
        else
        {
            OLED_DAT_0();
        }

        OLED_CLK_1();
        cmd <<= 1;
    }

    OLED_CS_1();
}
